#ifndef __ENTITY_H__
#define __ENTITY_H__

#include "Common.h"
#include "Graphics/Renderable.h"
#include "Utils/Singleton.h"

namespace RayFrame
{
  class DLL_DECLARE Entity;
  class DLL_DECLARE EntityManager;

  /// used for rendering, physics, script binding, etc.
  class DLL_DECLARE IEntityExtension
  {
  public:
    IEntityExtension(Entity* parent)
    {
      assert(parent);
      m_parent = parent;
    };

    virtual STRING GetTypeName() = 0;
    virtual void OnUpdate(double timeElapsed) = 0;
    virtual IEntityExtension* Clone() = 0;
  protected:
    Entity* m_parent;
  };

#define MAX_EXTENSION_COUNT 3
  typedef int EntityId;
  /** Object in scene. Just like entity in cryengine, and actor in unreal.
    TODO: Add properties for entity.
  **/
  class DLL_DECLARE Entity
  {
    friend class EntityManager;
  public:
    Entity(STRING extNames = "");
    ~Entity();

    bool SetEnable(bool e = true);
    bool GetEnable();

    virtual void OnUpdate(double timeElapsed);

    void SetPosition(glm::vec3 pos){ m_position = pos; };
    void SetRotation(glm::vec3 rot){ m_rotation = rot; };

    glm::mat4 GetWorldMatrix(){ return m_world; };
    glm::vec3 GetPosition(){ return m_position; };
    glm::vec3 GetRotation(){ return m_rotation; };

    EntityId GetEntityId(){ return m_entityId; };
    void GetEntityExtensions(IEntityExtension*** exts, int* extCount)
    {
      *exts = m_extensions;
      *extCount = m_extCount;
    }
  protected:
    glm::mat4 m_world;
    glm::vec3 m_position;
    glm::vec3 m_rotation;

    // variables below should initialized by entity manager
    IEntityExtension** m_extensions;
    int m_extCount;
    EntityId m_entityId;
    bool m_enabled;
  };

  /** Entity Manager
  **/
  class DLL_DECLARE EntityManager: public Singleton<EntityManager>
  {
  public:
    DECL_SINGLETON(EntityManager);

    EntityManager();
    ~EntityManager();

    void OnUpdate(double timeElapsed);

    /// return false if there's already a extension with the same type name.
    bool RegisterExtension(IEntityExtension* extension);
    IEntityExtension* FindExtension(STRING extName);

    /// while create an entity, it will automatically call RegisterEntity
    bool RegisterEntity(Entity* entity);
    Entity* FindEntity(EntityId id);
  private:
    EntityId GenerateEntityId();

  private:
    typedef std::map<EntityId, Entity*> MapEntity;
    typedef MapEntity::iterator IterEntity;

    typedef std::map<STRING, IEntityExtension*> MapExtension;
    typedef MapExtension::iterator IterExtension;

    MapEntity     m_entities;
    MapExtension  m_extensions;
  };
}
#endif
